unit DataBase;

interface
 uses
  ADODB, SysUtils, Controls,
  Forms, Dialogs, DB, Classes, ComObj,
  NCore, NBiometrics, NMedia, NUtils,IBQuery;

 const
  SEARCH_MODE_FULL = 0;
  SEARCH_MODE_FAST = 1;

 //-------------------------------------
 // Types
 //-------------------------------------

 type
  TTemplate = record
    FId: Integer;
    FTemplate: TArrayOfByte;
    FTemplateId: string;
   end;

  TTemplateArray = array of TTemplate;

  TSummary = record
   StartTime: TDateTime;
   StopTime: TDateTime;
   RunTime: Integer;

   RecordCount: Integer;
   MatchedRecordCount: Integer;
   FoundRecordCount: Integer;
  end;

  TDataBase = class(TObject)
   private
    //database: TSQLiteDatabase;
    Data: array [0..255] of TTemplateArray;
    count: Integer;

    procedure LoadData();
    procedure ClearData();

    function  AddToDataBase(templateId: string; biometricType: TNBiometricType; template: TArrayOfByte; image: TNImage; thumbnail: TNImage): Integer;
    procedure AddToImagesDatabase(templateId: Integer; biometricType: TNBiometricType; recordIndex: Integer; genIndex: Integer; frameIndex: Integer; image: TNImage);
    procedure IdentifyG(var summary: TSummary; matcher: TNMatcher; G: Byte; silentMode: Boolean; var found: boolean);
   public
    property TemplateCount: Integer read count;

    constructor Create();
    destructor  Destroy(); override;

    procedure Save();
    procedure Clear();

    procedure Append(biometricType: TNBiometricType; template: TTemplate; image: TNImage; thumbnail: TNImage);

    function  Identify(nfRec: TNFRecord; matcher: TNMatcher; matchingThreshold: Integer;
                   mode:Integer; silentMode: Boolean; var found: boolean): string;
  end;

implementation

//-------------------------------------
// Constants
//-------------------------------------
uses dbinv;

const
  PathToDb = '\Neurotechnology\FingersSample\';
  FileName = 'Fingers.db';

//-------------------------------------
// Misc functions
//-------------------------------------

procedure ByteArrayToStream(const A: TArrayOFByte; const Stream: TStream);
begin
  Stream.Position := 0;
  Stream.Size := Length(A);
  Stream.Write(Pointer(A)^, Length(A));
end;

procedure StreamToByteArray(const Stream: TStream; var A: TArrayOFByte);
begin
  Stream.Position := 0;
  SetLength(A, Stream.Size);
  Stream.Read(Pointer(A)^, Stream.Size);
end;

//-------------------------------------
// DataBase
//-------------------------------------

constructor TDataBase.Create();
begin
 inherited Create;
 LoadData();
end;

destructor TDataBase.Destroy;
begin
{ Save;
 ClearData();
 if (database <> nil)
 then
  begin
   FreeAndNil(database);
  end;
 inherited;   }
end;

procedure TDataBase.Save;
begin
 {database.ExecSQL('VACUUM');        }
end;

procedure TDataBase.Clear();
begin
{ database.ExecSQL('DELETE FROM Templates');
 ClearData;  }
end;

procedure TDataBase.ClearData();
 var i: Integer;
begin
 for i := 0 to 255 do
  begin
   SetLength(Data[i], 0);
  end;
 Count := 0;
end;
                {
procedure TDataBase.LoadData();
 var table: TSQLIteTable;
     templateRecord: TTemplate;
     position: integer;
     G : byte;
     rec: TNFRecord;
begin
 ClearData;
 table := nil;
 try
  table := database.GetTable('SELECT Id, TemplateId, Template FROM Templates');
  count := table.Count;
  table.MoveFirst;
  while (not table.EOF) do
  begin
   templateRecord.FTemplateId  := table.FieldAsString(1);
   templateRecord.FId := table.FieldAsInteger(0);
   StreamToByteArray(table.FieldAsBlob(2), templateRecord.FTemplate);
   rec := TNFRecord.CreateFromMemory(templateRecord.FTemplate);
   G := rec.G;
   FreeAndNil(rec);
   position := Length(Data[G]);
   SetLength(Data[G], position + 1);
   Data[G][position] := templateRecord;
   Count := Count + 1;
   table.Next;
  end;
 finally
  FreeAndNil(table);
 end;
end;         }


procedure TDataBase.LoadData();
var
  //table: TSQLIteTable;
  templateRecord: TTemplate;
  position: integer;
  G : byte;
  rec: TNFRecord;
  Stream  : TMemoryStream;
  bf      : TBlobField;
  AnArray : array[1..5000] of single; // A buffer to hold the data
  ArraySize:integer;
begin
 ClearData;
 try
  DMInv.DBRead.Connected := true;
  DMInv.IBTransaction1.Active := true;
  DMInv.IBQuery1.Active := true;
  DMInv.IBQuery1.Open;
  //count := DataModule1.IBQuery1.RecordCount;
  DMInv.IBQuery1.first;
  while (not DMInv.IBQuery1.EOF) do
  begin
   templateRecord.FTemplateId  := DMInv.IBQuery1.FieldByName('PID').AsString;
   templateRecord.FId := DMInv.IBQuery1.FieldByName('IDFINGER').AsInteger;
   //temp:= (DataModule1.IBQuery1.FieldByName('CTEMPLATE') as TBlobField);
   //tptBlob.size := length(tmp);
   //Move(PChar(tmp)^, tptBlob.tpt^, tptBlob.size);
   //move(temp,templateRecord.FTemplate,length(temp));
   //(FieldByName('ftemplate') as TBlobField)
   //StreamToByteArray((DataModule1.IBQuery1.FieldByName('CTEMPLATE') as TBlobField), templateRecord.FTemplate);
   //StreamToByteArray(temp, templateRecord.FTemplate);
   //tmp:=DataModule1.IBQuery1.FieldByName('CTEMPLATE').AsString;

   {StreamToByteArray((DataModule1.IBQuery1.FieldByName('CTEMPLATE') as TBlobField),
    templateRecord.FTemplate); }

    bf := DMInv.IBQuery1.FieldByName('CTEMPLATE') as TBlobField;
    Stream := TMemoryStream.Create;
    try
      bf.SaveToStream(Stream);
      ArraySize  := high(AnArray) * SizeOf(single);  // compute the maximum number of bytes to copy

      Stream.Position :=0;             // after loading stream, *position* is the last byte
      Stream.Read(AnArray, ArraySize); // copy up to "ArraySize" bytes to array

      StreamToByteArray((Stream), templateRecord.FTemplate);

      rec := TNFRecord.CreateFromMemory(templateRecord.FTemplate);
      G := rec.G;
      FreeAndNil(rec);
      position := Length(Data[G]);
      SetLength(Data[G], position + 1);
      Data[G][position] := templateRecord;
      Count := Count + 1;
   finally
      Stream.Free;
    end;
   DMInv.IBQuery1.Next;
  end;
 finally
 // FreeAndNil(table);
 end;
end;


procedure TDataBase.Append(biometricType: TNBiometricType; template: TTemplate; image: TNImage; thumbnail: TNImage);
 var position, Id: Integer;
     nfRecord: TNFRecord;
begin
 Id := AddToDataBase(template.FTemplateId, biometricType, template.FTemplate, image, thumbnail);
 if (Id >= 0) then
 begin
  try
    nfRecord := TNFRecord.CreateFromMemory(template.FTemplate);
    position := Length(Data[nfRecord.G]);
    SetLength(Data[nfRecord.G], position + 1);
    Data[nfRecord.G][position] := template;
    Count := Count + 1;
  finally
    FreeAndNil(nfRecord);
  end;
 end;
end;

procedure TDataBase.AddToImagesDatabase(templateId:    Integer; biometricType: TNBiometricType; recordIndex: Integer; genIndex: Integer; frameIndex: Integer; image: TNImage);
begin
end;


function TDataBase.AddToDataBase(templateId: string; biometricType: TNBiometricType; template: TArrayOfByte; image: TNImage; thumbnail: TNImage): Integer;
 var stream: TStream;
    streamImage: TStream;
    updQuery: TIBQuery;
    id:integer;
begin
 stream := TMemoryStream.Create;
 streamImage := TMemoryStream.Create;
 ByteArrayToStream(template, stream);
 //database.ParamsClear;
 //database.AddParamText(':1', templateId);
 //database.AddParamBlob(':2', stream);
 if (thumbnail <> nil) then
 begin
  ByteArrayToStream(TNPng.SaveImage(thumbnail), streamImage);
 // database.AddParamBlob(':3', streamImage);
  result := -1;
 end
 else
 begin
  updQuery := TIBQuery.Create(nil);

  updQuery.Database := DMInv.DBRead;
  updQuery.Transaction := DMInv.IBTransaction1;

  updQuery.SQL.Clear;

  updQuery.SQL.text := 'select GEN_ID(GEN_NEUROTEC_ID, 1) as tmpmax from rdb$database';

  updQuery.Prepare;
  updQuery.Open;

  id:=updQuery.FieldByName('tmpmax').AsInteger;
  if id=0 then id:=1;

  DMInv.IBQuery1.Open();
  DMInv.IBQuery1.Append();
  (DMInv.IBQuery1.FieldByName('ctemplate') as  TBlobField).LoadFromStream(stream);
  DMInv.IBQuery1.FieldByName('IDFINGER').AsInteger := id;
  DMInv.IBQuery1.FieldByName('pid').AsInteger := StrToInt(templateId);
   // qFinger.FieldByName('fuserenter').AsString := InvTypes.gUser;
   // qFinger.FieldByName('ftimeenter').AsDateTime := now;

  DMInv.IBQuery1.post();

  try
    if DMInv.IBTransaction1.InTransaction then DMInv.IBTransaction1.CommitRetaining;
    except
      on e: Exception do begin
        ShowMessage('Îøèáêà ñîõðàíåíèÿ:' + #13 + e.Message);
        if DMInv.IBTransaction1.InTransaction then DMInv.IBTransaction1.RollbackRetaining;
        Abort;
      end;
    end;
    updQuery.Free;
 result:=id;
 if (image <> nil) then
 begin
   AddToImagesDatabase(result, biometricType, 0, 0, 0, image);
 end;
 end;
end;
                        {
function TDataBase.AddToDataBase(templateId: string; biometricType: TNBiometricType; template: TArrayOfByte; image: TNImage; thumbnail: TNImage): Integer;
 var stream: TStream;
     streamImage: TStream;
     lastIndex: Integer;
begin
 stream := TMemoryStream.Create;
 streamImage := TMemoryStream.Create;
 ByteArrayToStream(template, stream);
 database.ParamsClear;
 database.AddParamText(':1', templateId);
 database.AddParamBlob(':2', stream);
 if (thumbnail <> nil) then
 begin
  ByteArrayToStream(TNPng.SaveImage(thumbnail), streamImage);
  database.AddParamBlob(':3', streamImage);
 end
 else
  database.AddParamNull(':3');
 database.ExecSQL('INSERT INTO Templates(Id, TemplateId, Template, Thumbnail) VALUES(NULL, :1, :2, :3)');
 lastIndex := database.GetLastInsertRowID;
 if (image <> nil) then
 begin
   AddToImagesDatabase(lastIndex, biometricType, 0, 0, 0, image);
 end;
 Result := lastIndex;
end;        }

procedure ClearSummary(var summary: TSummary);
begin
  summary.RunTime            := 0;
  summary.RecordCount        := 0;
  summary.MatchedRecordCount := 0;
  summary.FoundRecordCount   := 0;
end;

procedure CalculateTime(var summary: TSummary; IncreaseMatchedCount: boolean);
 var tempTime: TDateTime;
 myHour, myMin, mySec, myMilli : Word;
begin
  tempTime := summary.StartTime - summary.StopTime;
  DecodeTime(tempTime,myHour, myMin, mySec, myMilli);
  // hours not necessary
  summary.RunTime := summary.RunTime + myHour*60*60 + myMin*60 + mySec;
  If IncreaseMatchedCount Then summary.MatchedRecordCount := summary.MatchedRecordCount + 1;
end;

function PrintRaportInfo(summary: TSummary): string;
 var str: string;
begin
 str := 'Fingerprint identification results:'+ CRLF +'------------------------------------'+CRLF+
        'Time: ' + IntToStr(summary.RunTime) + ' s' + CRLF + 'Found fingerprint count: ' + IntToStr(summary.FoundRecordCount) +
        CRLF + 'Db fingerprint count: ' + IntToStr(summary.RecordCount) + CRLF + 'Matched fingerprint count: ' + IntToStr(summary.MatchedRecordCount);
 if (summary.RunTime > 0 ) then str := str + 'Matching speed: ' + FloatToStr(summary.MatchedRecordCount / summary.RunTime) + ' fingerprints/s' +
                                       'Effective matching speed: ' + FloatToStr(summary.RecordCount / summary.RunTime) + ' fingerprints/s';
 str := str + CRLF+'------------------------------------' + CRLF;
 PrintRaportInfo := str;
end;

procedure TDatabase.IdentifyG(var summary: TSummary; matcher: TNMatcher; G: Byte; silentMode: Boolean; var found: boolean);
 var score, j, temp: Integer;
begin
 temp := Length(Data[G]);
 if (temp > 0 ) then
  for j:=0 to temp - 1 do
   begin
    summary.StartTime := Now;
    score := matcher.IdentifyNext(Data[G][j].FTemplate);
    summary.StopTime := Now;
    CalculateTime(summary, true);
                 // threshold
    if (score >= matcher.MatchingThreshold) then
    begin
     found := true;

     summary.FoundRecordCount := summary.FoundRecordCount + 1;
    end;
   end;
end;

 function TDatabase.Identify(nfRec: TNFRecord; matcher: TNMatcher; matchingThreshold: Integer;
                   mode:Integer; silentMode: Boolean; var found: boolean): string;
 var G: Byte;
     score: Integer;
     packedNFRecord: TArrayOfByte;
     mdFirst: TNMMatchDetails;
     i, j: Integer;
     rec: TTemplate;
     strReturn: string;
     // additional
     Gmin, Gmax: Byte;
     updir: Boolean;
     summary: TSummary;
begin
  ///if DMInv.pidNecro > 0 then exit;

  strReturn := '';
  DMInv.pidNecroNot := 1;
  packedNFRecord := nfRec.Save;
  G := nfRec.G();

  // Clearing summary
  ClearSummary(summary);

  summary.RecordCount := count;

  summary.StartTime := Now;
  matcher.IdentifyStart(packedNFRecord);
  summary.StopTime := Now;

  CalculateTime(summary, false);
  
  Mode:=0;
  if (Mode =  SEARCH_MODE_FAST) then
  begin
   found := False;
   updir := True;

   Gmin := G;
   Gmax := G;

   while (not found) do
   begin
     IdentifyG(summary, matcher, G, found, SilentMode);     
     if (not found) then
     begin
      if ((Gmin = 0) and (Gmax = 255)) then
       found := true
      else
       begin
        if ((updir = true) and (Gmax = 255)) then updir := false;
        if ((not updir ) and ( Gmin = 0 )) then updir := true;
       end;
      if (not found) then
       if (updir) then
        begin
         Gmax := Gmax + 1;
         G := Gmax;
        end
       else
        begin
         Gmin := Gmin - 1;
         G := Gmin;
        end;
      updir := Not updir;
     end;
   end;
  end
  else
   begin
    for i := 0 to 255 do
    begin
     if (Length(Data[i]) <> 0) then
      begin
       for j := 0 to Length(Data[i]) - 1 do
        begin
          mdFirst.score := 0;
          packedNFRecord := Data[i][j].FTemplate;
          summary.StartTime := Now;
          score := matcher.IdentifyNext(packedNFRecord);
          summary.StopTime := Now;
          CalculateTime(summary, true);

          if (score >= matchingThreshold) then
          begin
           rec := Data[i][j];

           DMInv.pidNecro := StrToInt(rec.FTemplateId);
           DMInv.pidnecroNot := 0;
           strReturn := strReturn + 'Id:' + IntToStr(rec.FId) + CRLF + 'FingerID: ' + rec.FTemplateId
            + CRLF + ' Score: ' + IntToStr(score) + ' Threshold: ' + IntToStr(matchingThreshold) + CRLF;
           summary.FoundRecordCount := summary.FoundRecordCount + 1;

           //if (not matcher.MatchAll) then break;
          end;
        end;                                                                
       // if (score >= matchFAR) and (not matcher.MatchAll) then break;
      end;
    end;
   end;

  matcher.IdentifyEnd;
    
  // MainForm.EndLongProcess
  found := summary.FoundRecordCount > 0;     
  If Not SilentMode Then Result := strReturn + PrintRaportInfo(summary)
   else Result := '';    
 { Result := strReturn + PrintRaportInfo(summary)   }
end;

      {
//-------------------------------------
// Types
//-------------------------------------

type
  TSummary = record
   StartTime: TDateTime;
   StopTime: TDateTime;
   RunTime: Integer;

   RecordCount: Integer;
   MatchedRecordCount: Integer;
   FoundRecordCount: Integer;
  end;

  TFingerprintRecord = class (TObject)
   public
    id: Integer;
    Features: TArrayOfByte;
    FingerId: string;
   end;

  TFingerprintArray = array of TFingerprintRecord;
                                                   }
//-------------------------------------
// Variables
//-------------------------------------
{
var
  summary: TSummary;
  Data: array [0..255] of TFingerprintArray;
  
//-------------------------------------
// Additional functions
//-------------------------------------

procedure ClearSummary();
begin
  summary.RunTime            := 0;
  summary.RecordCount        := 0;
  summary.MatchedRecordCount := 0;
  summary.FoundRecordCount   := 0;
end;

procedure CalculateTime(IncreaseMatchedCount: boolean);
 var tempTime: TDateTime;
 myHour, myMin, mySec, myMilli : Word;
begin
  tempTime := summary.StartTime - summary.StopTime;
  DecodeTime(tempTime,myHour, myMin, mySec, myMilli);
  // hours not necessary
  summary.RunTime := summary.RunTime + myHour*60*60 + myMin*60 + mySec;
  If IncreaseMatchedCount Then summary.MatchedRecordCount := summary.MatchedRecordCount + 1;
end;

function PrintRaportInfo(): string;
 var str: string;
begin
 str := 'Fingerprint identification results:'+ CRLF +'------------------------------------'+CRLF+
        'Time: ' + IntToStr(summary.RunTime) + ' s' + CRLF + 'Found fingerprint count: ' + IntToStr(summary.FoundRecordCount) +
        CRLF + 'DB fingerprint count: ' + IntToStr(summary.RecordCount) + CRLF + 'Matched fingerprint count: ' + IntToStr(summary.MatchedRecordCount);
 if (summary.RunTime > 0 ) then str := str + 'Matching speed: ' + FloatToStr(summary.MatchedRecordCount / summary.RunTime) + ' fingerprints/s' +
                                       'Effective matching speed: ' + FloatToStr(summary.RecordCount / summary.RunTime) + ' fingerprints/s';
 str := str + CRLF+'------------------------------------' + CRLF;
 PrintRaportInfo := str;
end;

function FileSize(FileName : String) : Int64;
var
  SearchRec : TSearchRec;
begin
  if FindFirst(FileName, faAnyFile, SearchRec ) = 0 then
     Result := Int64(SearchRec.FindData.nFileSizeHigh) shl Int64(32) +
               Int64(SearchREc.FindData.nFileSizeLow)
  else
     Result := 0;
  SysUtils.FindClose(SearchRec);
end;

procedure DeleteArrayItem(var X: TFingerprintArray; const Index: Integer) ;
begin
   if Index > High(X) then Exit;
   if Index < Low(X) then Exit;
   if Index = High(X) then
   begin
     SetLength(X, Length(X) - 1) ;
     Exit;
   end;
   //Finalize(X[Index]) ;
   System.Move(X[Index +1], X[Index],(Length(X) - Index -1) * SizeOf(X[Index]) + 1) ;
   SetLength(X, Length(X) - 1) ;
end;
                      }


end.
